(**************************************************************************)
(*       ___                                                              *)
(*      ||M||                                                             *)
(*      ||A||       A project by Andrea Asperti                           *)
(*      ||T||                                                             *)
(*      ||I||       Developers:                                           *)
(*      ||T||         The HELM team.                                      *)
(*      ||A||         http://helm.cs.unibo.it                             *)
(*      \   /                                                             *)
(*       \ /        This file is distributed under the terms of the       *)
(*        v         GNU General Public License Version 2                  *)
(*                                                                        *)
(**************************************************************************)

include "basics/types.ma".

(* Most of the types we have seen so far are enumerated types, composed by a 
finite set of alternatives, and records, composed by tuples of heteregoneous 
elements. A more interesting case of type definition is when some of the rules 
defining its elements are recursive, i.e. they allow the formation of more 
elements of the type in terms of the already defined ones. The most typical case 
is provided by the natural numbers, that can be defined as the smallest set 
generated by a constant 0 and a successor function from natural numbers to natural
numbers *)

inductive nat : Type[0] ≝ 
| O :nat
| S: nat →nat.

(* The two terms O and S are called constructors: they define the signature of the
type, whose objects are the elements freely generated by means of them. So, 
examples of natural numbers are O, S O, S (S O), S (S (S O)) and so on. 

The language of Matita allows the definition of well founded recursive functions 
over inductive types; in order to guarantee termination of recursion you are only 
allowed to make recursive calls on structurally smaller arguments than the ones 
you received in input. Most mathematical functions can be naturally defined in this
way. For instance, the sum of two natural numbers can be defined as follows *)

let rec add n m ≝ 
match n with
[ O ⇒ m
| S a ⇒ S (add a m)
].

(*
Elimination

It is worth to observe that the previous algorithm works by recursion over the
first argument. This means that, for instance, (add O x) will reduce to x, as 
expected, but (add x O) is stuck. 
How can we prove that, for a generic x, (add x O) = x? The mathematical tool to do 
it is called induction. The induction principle states that, given a property P(n) 
over natural numbers, if we prove P(0) and prove that, for any m, P(m) implies P(S m), 
than we can conclude P(n) for any n. 

The elim tactic, allow you to apply induction in a very simple way. If your goal is 
P(n), the invocation of
  elim n
will break down your task to prove the two subgoals P(0) and ∀m.P(m) → P(S m).

Let us apply it to our case *)

lemma add_0: ∀a. add a O = a.
#a elim a

(* If you stop the computation here, you will see on the right the two subgoals 
    - add O O = O
    - ∀x. add x 0 = x → add (S x) O = S x
After normalization, both goals are trivial.
*)

normalize // qed.

(* In a similar way, it is convenient to state a lemma about the behaviour of 
add when the second argument is not zero. *)

lemma add_S : ∀a,b. add a (S b) = S (add a b).

(* In the same way as before, we proceed by induction over a. *)

#a #b elim a normalize //
qed. 

(* We are now in the position to prove the commutativity of the sum *)

theorem add_comm : ∀a,b. add a b = add b a.

#a elim a normalize

(* We have two sub goals:
  G1: ∀b. b = add b O
  G2: ∀x.(∀b. add x b = add b x) → ∀b. S (add x b) = add b (S x). 
G1 is just our lemma add_O. For G2, we start introducing x and the induction 
hypothesis IH; then, the goal is proved by rewriting using add_S and IH.

Namely:
[ #H1 @(sym_eq ?) @(add_0)
| #X #IH #B >add_S @(eq_f) @IH ]

For Matita, the task is trivial and we can simply close the goal with // *)


// qed.

(* COERCIONS *)

inductive bool : Type[0] ≝
| tt : bool
| ff : bool.

definition nat_of_bool ≝ λb. match b with 
[ tt ⇒ S O 
| ff ⇒ O 
].

(* coercion nat_of_bool. ?? *)
 
(* Let us now define the following function: *)

definition twice ≝ λn.add n n. 

(* 
Existential

We are interested to prove that for any natural number n there exists a natural 
number m that is the integer half of n. This will give us the opportunity to 
introduce new connectives and quantifiers and, later on, to make some interesting 
consideration on proofs and computations. *)

theorem ex_half: ∀n.∃m. n = twice m ∨ n = S (twice m).
#n elim n normalize

(* We proceed by induction on n, that breaks down to the following goals:
  G1: ∃m.O = add O O ∨ O = S (add m m)
  G2: ∀x.(∃m. x = add m m ∨ x = S (add m m))→ ∃m. S x = add m m ∨ S x = S (add m m)
The only way we have to prove an existential goal is by exhibiting the witness, 
that in the case of first goal is O. We do it by apply the term called ex_intro 
instantiated by the witness. Then, it is clear that we must follow the left branch 
of the disjunction. One way to do it is by applying the term or_introl, that is 
the first constructor of the disjunction. However, remembering the names of 
constructors can be annyoing: we can invoke the application of the n-th 
constructor of an inductive type (inferred by the current goal) by typing %n. At 
this point we are left with the subgoal O = add O O, that is closed by 
computation. It is worth to observe that invoking automation at depth /3/ would 
also automatically close G1.
*) 

  [@(ex_intro … O) %1 //

(* 
Destructuration

The case of G2 is more complex. We should start introducing x and the 
inductive hypothesis
     IH: ∃m. x = add m m ∨ x = S (add m m) 
At this point we should assume the existence of m enjoying the inductive 
hypothesis. To eliminate the existential from the context we can just use the 
case tactic. This situation where we introduce something into the context and 
immediately eliminate it by case analysis is so frequent that Matita provides a 
convenient shorthand: you can just type a single "*". 
The star symbol should be reminiscent of an explosion: the idea is that you have
a structured hypothesis, and you ask to explode it into its constituents. In the 
case of the existential, it allows to pass from a goal of the shape 
    (∃x.P x) → Q
to a goal of the shape
    ∀x.P x → Q
*)
  |#x * 
(* At this point we are left with a new goal with the following shape
  G3: ∀m. x = add m m ∨ x = S (add m m) → ....  
We should introduce m, the hypothesis H: x = add m m ∨ x = S (add m m), and 
then reason by cases on this hypothesis. It is the same situation as before: 
we explode the disjunctive hypothesis into its possible consituents. In the case 
of a disjunction, the * tactic allows to pass from a goal of the form
    A∨B → Q
to two subgoals of the form
    A → Q  and  B → Q
*)
  #m * #eqx
(* In the first subgoal, we are under the assumption that x = add m m. The half 
of (S x) is hence m, and we have to prove the right branch of the disjunction. 
In the second subgoal, we are under the assumption that x = S (add m m). The halh 
of (S x) is hence (S m), and have to follow the left branch of the disjunction.
*)
  [@(ex_intro … m) /2/ | @(ex_intro … (S m)) normalize  /2/
  ]
qed. 

(* 
Computing vs. Proving

Instead of proving the existence of a number corresponding to the half of n, 
we could be interested in computing it. The best way to do it is to define this 
division operation together with the remainder, that in our case is just a 
boolean value: tt if the input term is even, and ff if the input term is odd. 
Since we must return a pair, we could use a suitably defined record type, or 
simply a product type nat × bool, defined in the basic library. The product type 
is just a sort of general purpose record, with standard fields fst and snd, called 
projections. 
A pair of values n and m is written (pair … m n) or \langle n,m \rangle - visually 
rendered as 〈n,m〉 

We first write down the function, and then discuss it.*)

let rec div2 n ≝ 
match n with
[ O ⇒ 〈O,ff〉
| S a ⇒ 
   let 〈q,r〉 ≝ (div2 a) in
   match r with
   [ tt ⇒ 〈S q,ff〉 
   | ff ⇒ 〈q, tt〉
   ]
]. 

(* The function is computed by recursion over the input n. If n is 0, then the 
quotient is 0 and the remainder is ff. If n = S a, we start computing the half 
of a, say 〈q,b〉. Then we have two cases according to the possible values of b: 
if b is tt, then we must return 〈q,ff〉, while if b = ff then we must return 
〈S q,tt〉.

It is important to point out the deep, substantial analogy between the algorithm 
for computing div2 and the the proof of ex_half. In particular ex_half returns a 
proof of the kind ∃n.A(n)∨B(n): the really informative content in it is the 
witness n and a boolean indicating which one between the two conditions A(n) and 
B(n) is met. This is precisely the quotient-remainder pair returned by div2.
In both cases we proceed by recurrence (respectively, induction or recursion) over 
the input argument n. In case n = 0, we conclude the proof in ex_half by providing 
the witness O and a proof of A(O); this corresponds to returning the pair 〈O,ff〉 in 
div2. Similarly, in the inductive case n = S a, we must exploit the inductive 
hypothesis for a (i.e. the result of the recursive call), distinguishing two subcases 
according to the the two possibilites A(a) or B(a) (i.e. the two possibile values of 
the remainder for a). The reader is strongly invited to check all remaining details.

Let us now prove that our div2 function has the expected behaviour.
*)

lemma div2SO: ∀n,q. div2 n = 〈q,ff〉 → div2 (S n) = 〈q,tt〉.
#n #q #H normalize >H normalize // qed.

lemma div2S1: ∀n,q. div2 n = 〈q,tt〉 → div2 (S n) = 〈S q,ff〉.
#n #q #H normalize >H normalize // qed.

lemma div2_ok: ∀n,q,r. div2 n = 〈q,r〉 → n = add (nat_of_bool r) (twice q).
#n elim n
  [#q #r #H normalize in H; destruct  //
  |#a #Hind #q #r
   cut (div2 a = 〈fst … (div2 a), snd … (div2 a)〉) [//] 
   cases (snd … (div2 a))
    [#H >(div2S1 … H) #H1 destruct normalize @eq_f >add_S @(Hind … H)
    |#H >(div2SO … H) #H1 destruct normalize @eq_f @(Hind … H) 
    ]
qed.

(* 
Mixing proofs and computations

There is still another possibility, however, namely to mix the program and its 
specification into a single entity. The idea is to refine the output type of the 
div2 function: it should not be just a generic pair 〈q,r〉 of natural numbers but a 
specific pair satisfying the specification of the function. In other words, we need 
the possibility to define, for a type A and a property P over A, the subset type 
{a:A|P(a)} of all elements a of type A that satisfy the property P. Subset types 
are just a particular case of the so called dependent types, that is types that 
can depend over arguments (such as arrays of a specified length, taken as a 
parameter).These kind of types are quite unusual in traditional programming 
languages, and their study is one of the new frontiers of the current research on 
type systems. 

There is nothing special in a subset type {a:A|P(a)}: it is just a record composed 
by an element of a of type A and a proof of P(a). The crucial point is to have a 
language reach enough to comprise proofs among its expressions. 
*)

record Sub (A:Type[0]) (P:A → Prop) : Type[0] ≝
  {witness: A; 
   proof: P witness}.

definition qr_spec ≝ λn.λp.∀q,r. p = 〈q,r〉 → n = add (nat_of_bool r) (twice q).
  
(* We can now construct a function from n to {p|qr_spec n p} by composing the objects
we already have *)

definition div2P: ∀n. Sub (nat×bool) (qr_spec n) ≝ λn.
 mk_Sub ?? (div2 n) (div2_ok n).

(* But we can also try do directly build such an object *)

definition div2Pagain : ∀n.Sub (nat×bool) (qr_spec n).
#n elim n
  [@(mk_Sub … 〈O,ff〉) normalize #q #r #H destruct //
  |#a * #p #qrspec 
   cut (p = 〈fst … p, snd … p〉) [//] 
   cases (snd … p)
    [#H @(mk_Sub … 〈S (fst … p),ff〉) #q #r #H1 destruct @eq_f >add_S @(qrspec … H)
    |#H @(mk_Sub … 〈fst … p,tt〉) #q #r #H1 destruct @eq_f @(qrspec … H) 
  ]
qed.

example full7: (div2Pagain (S(S(S(S(S(S(S O)))))))) = ?.
[normalize
// qed.

example quotient7: witness … (div2Pagain (S(S(S(S(S(S(S O)))))))) = ?.
normalize // qed.

example quotient8: witness … (div2Pagain (twice (twice (twice (twice (S O)))))) 
       = 〈twice (twice (twice (S O))), ff〉.
// qed. 
